/*
 * Project: push-center-service-impl
 * 
 * File Created at 2014-10-9
 * 
 * Copyright 2012 Greenline.com Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Greenline Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Greenline.com.
 */
package com.greenline.push.service.core.send;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.greenline.apns.dataobject.APNSPushGroupMsgDO;
import com.greenline.apns.dataobject.APNSPushMsgDO;
import com.greenline.apns.dataobject.PushResult;
import com.greenline.apns.push.APNSMsgPushService;
import com.greenline.gateway.base.ResponseCode;
import com.greenline.gateway.base.ResponseDTO;
import com.greenline.guahao.user.model.bo.UserAppDeviceBO;
import com.greenline.guahao.user.service.share.UserAppDeviceService;
import com.greenline.hrs.util.core.bo.response.Response;
import com.greenline.push.core.enums.Channels;
import com.greenline.push.core.enums.MsgSendType;
import com.greenline.push.core.enums.Tasks;
import com.greenline.push.core.pushcenter.MsgRetryFullManager;
import com.greenline.push.service.core.exception.ChannelSendException;
import com.greenline.push.service.core.item.AppPacket;
import com.greenline.push.service.core.item.Packet;
import com.greenline.push.service.core.send.base.AbstractSender;
import com.greenline.push.service.core.send.base.AppPacketHandler;
import com.greenline.push.service.core.tmpl.AppChannelCache;
import com.greenline.push.service.core.tmpl.GroupAppCache;
import com.greenline.push.service.core.utils.Constants;

/**
 * APNSSender
 * 
 * @author shuai.zuo
 * @date 2014-10-9
 * @version V1.0
 */
public class APNSSender extends AbstractSender {

    @Resource
    private APNSMsgPushService apnsMsgPushService;
    @Resource
    private UserAppDeviceService userAppDeviceService;
    @Resource
    private AppChannelCache appChannelCache;
    @Resource
    private GroupAppCache groupAppCache;
    @Resource
    private AppPacketHandler packetHandler;
    @Resource
    private MsgRetryFullManager retryFullManager;
    Logger log = Constants.LOG_PROCESS;

    @Override
    public Channels supportChannel() {
        return Channels.apns;
    }

    @Override
    protected boolean doSend(Packet packet) {
        AppPacket appPacket = (AppPacket) packet;
        log.info(String
                .format("{apnssender收到数据}:准备发送***domainId:[%s].tempId:[%s].type:[%s],extparam-context:[%s-%s],source:[%s],retryId:[%s],thread:[%s]",
                        appPacket.getDomainId(), appPacket.getTemplateId(), appPacket.getType(), appPacket.getExtParam(),
                        appPacket.getContext(), appPacket.getSource(), appPacket.getDbRetryId(), Thread.currentThread().getName()));

        ResponseDTO<Boolean> result = null;
        try {
            // 模版发送
            if (MsgSendType.POINT.getType() == appPacket.getType()) {
                result = this.sendPoint(appPacket);
            } else
            // 全部发送
            if (MsgSendType.ALL.getType() == appPacket.getType()) {
                result = this.sendAll(appPacket);
            } else
            // 分组发送
            if (MsgSendType.GROUP.getType() == appPacket.getType()) {
                result = this.sendGroup(appPacket);
            } else if (MsgSendType.UPLOAD.getType() == appPacket.getType() || MsgSendType.CONDITIONS.getType() == appPacket.getType()
                    || MsgSendType.DOMAIN.getType() == appPacket.getType()) {
                // upload 或者 condition 或者 domain 类型 可以调用第三方按组发送
                this.sendList(appPacket);
                return true;
            }
        } catch (Exception e) {
            result = new ResponseDTO<Boolean>();
            result.setResult(ResponseCode.ERROR, e.getMessage());
            packetHandler.afterSendOperation(appPacket, false);

            log.debug(String.format("{apnsSender发送异常}错误信息:%s.相关参数:domainId:%s.tempId:%s.extparam:%s", e.getMessage(),
                    appPacket.getDomainId(), appPacket.getTemplateId(), appPacket.getExtParam()));
        }
        boolean res = result.getDataResult();

        // log.debug(String.format("{apnssender发送完成}:结果:%s***domainId:%s.tempId:%s.extparam:%s",
        // res, appPacket.getDomainId(),
        // appPacket.getTemplateId(), appPacket.getExtParam()));

        return res;
    }

    // call sendList
    private void sendList(AppPacket appPacket) {
        String methodName = "{apns.sendList}";
        Long retryId = appPacket.getDbRetryId();

        List<String> sResult = getUserIdsByRetryId(retryId);

        if (sResult == null) {
            log.error(String.format("%s发送失败,apppacket下没有取到用户.retryId:%s,context:%s", methodName, retryId, appPacket.getContext()));
            packetHandler.afterSendOperation(appPacket, false, true);
            return;
        }
        log.debug(String.format("%s批量发送echat消息.uidlist:%s.contenxt:%s", methodName, sResult.toString(), appPacket.getContext()));
        List<Long> paramList = new ArrayList<Long>();
        for (String uid : sResult) {
            paramList.add(Long.valueOf(uid));
        }
        // send
        sendWithUserList(appPacket, paramList, methodName);
    }

    // send group
    private ResponseDTO<Boolean> sendGroup(AppPacket appPacket) {
        String methodName = "{apns.sendGroup}";
        ResponseDTO<Boolean> result = new ResponseDTO<Boolean>();
        Boolean res = false;
        // log
        log.debug(String.format("%s开始推送:context:[%s],gid:[%s]", methodName, appPacket.getContext(), appPacket.getRecvUserId()));

        String relativeGroupId = groupAppCache.loadByGroupId(appPacket.getAppChannelId(), appPacket.getRecvUserId());
        if (StringUtils.isBlank(relativeGroupId)) {
            Long gid = appPacket.getRecvUserId();
            List<Long> userIds = getUserIdsbyGid(gid);
            // send
            sendWithUserList(appPacket, userIds, methodName);
        } else {
            // send group directly
            log.error(String.format("%s调用群发失败.:原因:%s.gid:%s,appchannelId:%s", methodName, "没有快捷群发接口", appPacket.getRecvUserId(),
                    appPacket.getAppChannelId()));
        }
        // if called remote false

        result.setDataResult(res);
        return result;
    }

    // do send
    private void sendWithUserList(AppPacket appPacket, List<Long> sResult, String methodName) {
        String extAppId = appChannelCache.getExtAppId(appPacket.getAppChannelId());
        String appId = appPacket.getAppId();
        // set uid
        appPacket.setUserids(sResult.toString());
        // get token
        Response<List<UserAppDeviceBO>> userResultList = null;
        try {
            userResultList = userAppDeviceService.listByUserIds(sResult, appId);
        } catch (Exception e1) {
            packetHandler.afterSendOperation(appPacket, false);
            log.error(String.format(
                    "%s调用userAppDeviceService.listByUserIds接口异常.userids:[%s],appid:[%s],errMsg:[%s],appId:[%s],dbretryId:[%s]", methodName,
                    sResult, appId, e1.getMessage(), appPacket.getDbRetryId()));
            return;
        }
        List<UserAppDeviceBO> userList = userResultList.getData();
        if (null == userList || userList.size() == 0) {
            log.error(String.format("%s调用userAppDeviceService.listByUserIds获取到用户列表为空.messageId:[%s],userIds:[%s],appId:[%s],retryId:[%s]",
                    methodName, appPacket.getMessageId(), sResult, appId, appPacket.getDbRetryId()));
            packetHandler.afterSendOperation(appPacket, false, true);
            return;
        }
        // build data
        List<APNSPushMsgDO> pushdataList = new ArrayList<APNSPushMsgDO>();
        for (UserAppDeviceBO userAppDeviceBO : userList) {
            if (StringUtils.isNotBlank(userAppDeviceBO.getDeviceToken())) {
                APNSPushMsgDO tmp = new APNSPushMsgDO();
                tmp.setDeviceToken(userAppDeviceBO.getDeviceToken());
                pushdataList.add(tmp);
            }
        }
        if (pushdataList.size() == 0) {
            log.error(String.format(
                    "%s调用userAppDeviceService.listByUserIds获取到用户列表token为空.messageId:[%s],userIds:[%s],appId:[%s],retryId:[%s]", methodName,
                    appPacket.getMessageId(), sResult, appId, appPacket.getDbRetryId()));
            packetHandler.afterSendOperation(appPacket, false, true);
            return;
        }

        // here is the whole message with title+content,must split
        String context = appPacket.getContext();
        String title = null;
        String pushdata = null;
        try {
            title = context.substring(0, context.indexOf(Constants.APNS_CONTEXT_SPLIT));
            pushdata = context.substring(context.indexOf(Constants.APNS_CONTEXT_SPLIT) + 2, context.length());
            log.debug(String.format("%s解析title和pushdata完成,title:%s.pushdata:%s,context:[%s],retryId:[%s]", methodName, title, pushdata,
                    context, appPacket.getDbRetryId()));
        } catch (Exception e) {
            e.printStackTrace();
            title = context;
            pushdata = context;
            log.warn(String.format("%s解析title和pushdata失败,title:%s.pushdata:%s,context:%s,errMsg:%s", methodName, title, pushdata, context,
                    e.getMessage()));
        }
        if (title.length() > 30) {
            title = title.substring(0, 30) + "...";
        }
        String totalLength = title + pushdata;

        if (totalLength.getBytes().length > 256) {
            log.error(String.format("%s消息长度超过限制.不能超过256个字节.不再进行下一步处理,title:[%s].pushdata:[%s],context:[%s],retryId:[%s]", methodName,
                    title, pushdata, context, appPacket.getDbRetryId()));
            // delete
            packetHandler.afterSendOperation(appPacket, false, true);
            return;
        }
        APNSPushGroupMsgDO msg = new APNSPushGroupMsgDO();
        msg.setAlertContent(title);
        msg.setIosPushData(pushdata);
        msg.setTokenList(pushdataList);
        msg.setAppId(extAppId);
        PushResult result = null;
        // handle exception
        try {
            log.debug(String.format("%s.调用群发 .appId:[%s],extappId:[%s],context:[%s]", methodName, appId, extAppId, appPacket.getContext()));
            result = apnsMsgPushService.pushAPNSGroupMessage(msg);
        } catch (Exception e) {
            packetHandler.afterSendOperation(appPacket, false);
            log.error(String.format("%s.远程接口调用失败,异常信息:[%s].内容提:[%s],retryId:[%s]", methodName, e.getMessage(), appPacket.getContext(),
                    appPacket.getDbRetryId()));
            return;
        }

        boolean flag = false;
        String errorCode = result.getErrorCode();
        if (errorCode.equals("0")) {
            flag = true;
            //
            packetHandler.afterSendOperation(appPacket, flag);
        } else {
            flag = false;
            packetHandler.afterSendOperation(appPacket, flag, true);
        }
        log.info(String.format("%s推送结果:[%s],messageId:[%s],详情:[%s],内容:[%s],domain&template:[%s-%s],用户列表:[%s],retryId:[%s]", methodName,
                flag, appPacket.getMessageId(), result.getDesc(), appPacket.getContext(), appPacket.getDomainId(),
                appPacket.getTemplateId(), sResult.toString(), appPacket.getDbRetryId()));
    }

    /**
     * 发送点对点消息
     * 
     * @param appPacket
     * @return
     * @throws ChannelSendException
     */
    private ResponseDTO<Boolean> sendPoint(AppPacket appPacket) throws ChannelSendException {
        ResponseDTO<Boolean> result = new ResponseDTO<Boolean>();
        Boolean res = false;
        try {
            res = sendPointSync(appPacket);
        } catch (Exception e) {
            // 该app packet在sendPointSync 中已处理.此处不需要处理
            result.setMessage("推送失败");
            result.setError(e.getMessage());
            log.error(String.format("{apnssender调用sendPoint异常.}.receivedUserid:%s,appchannelId:%s,异常信息:%s", appPacket.getRecvUserId(),
                    appPacket.getAppChannelId(), e.getMessage()));
        }
        result.setDataResult(res);
        // result.setDataResult(true);
        return result;
    }

    // 全部发送
    private ResponseDTO<Boolean> sendAll(AppPacket appPacket) {
        ResponseDTO<Boolean> result = null;
        return result;
    }

    private boolean sendPointSync(AppPacket packet) throws ChannelSendException {
        String methodName = "{apns.sendPointSync}";
        boolean flag = false;
        String extAppId = appChannelCache.getExtAppId(packet.getAppChannelId());
        String appId = packet.getAppId();
        if (extAppId == null) {
            // delete directly
            packetHandler.afterSendOperation(packet, false, true);
            throw new ChannelSendException("应用：" + packet.getAppId() + "不存在。");
        }

        UserAppDeviceBO userAppDevice = new UserAppDeviceBO();
        userAppDevice.setAppId(appId);
        userAppDevice.setUid(packet.getRecvUserId());
        userAppDevice.setIsOnline(true);
        Response<List<UserAppDeviceBO>> tokenList = null;
        try {
            tokenList = userAppDeviceService.list(userAppDevice);
        } catch (Exception e) {
            log.error(String.format("%s调用提取token接口失败.异常信息:%s", methodName, e.getMessage()));
            packetHandler.afterSendOperation(packet, false);
            return flag;
        }
        if (tokenList == null || tokenList.getData() == null || tokenList.getData().isEmpty()) {
            log.error(String.format("%s获取token的list为空.appid:[%s],userid:[%s],retryId:[%s]", methodName, appId, packet.getRecvUserId(),
                    packet.getDbRetryId()));
            // delete directly
            packetHandler.afterSendOperation(packet, false, true);
            return flag;
        }
        UserAppDeviceBO deviceToken = tokenList.getData().get(0);
        String token = deviceToken.getDeviceToken();
        //
        if (StringUtils.isBlank(token)) {
            log.error(String.format("%s获取token为空.appid:[%s],userid:[%s],retryId:[%s]", methodName, appId, packet.getRecvUserId(),
                    packet.getDbRetryId()));
            // delete directly
            packetHandler.afterSendOperation(packet, false, true);
            return flag;
        }
        com.greenline.apns.dataobject.APNSPushMsgDO msg = new com.greenline.apns.dataobject.APNSPushMsgDO();
        // ApnsPushMsgPacket msg = new ApnsPushMsgPacket();
        // String token =
        // "d2c20c8cc80c720a9005a82978e650191c1f312e83b1d4def8be22c63a06a17a";

        // here is the whole message with title+content,must split
        String context = packet.getContext();
        String title = null;
        String pushdata = null;
        try {
            title = context.substring(0, context.indexOf(Constants.APNS_CONTEXT_SPLIT));
            pushdata = context.substring(context.indexOf(Constants.APNS_CONTEXT_SPLIT) + 2, context.length());
        } catch (Exception e) {
            e.printStackTrace();
            title = context;
            pushdata = context;
            log.warn(String.format("%s解析title和pushdata失败,默认将title和content设为context ,title:%s.pushdata:%s,context:%s", methodName, title,
                    pushdata, context));
        }
        // cut
        if (title.length() > 30) {
            title = title.substring(0, 30) + "...";
        }
        String totalContent = title + pushdata;

        if (totalContent.getBytes().length > 256) {
            log.error(String.format("%s消息长度超过限制.不能超过256个字节.不再进行下一步处理,title:[%s].pushdata:[%s],context:[%s],retryId:[%s]", methodName,
                    title, pushdata, context, packet.getDbRetryId()));
            // delete
            packetHandler.afterSendOperation(packet, false, true);
            return false;
        }

        msg.setAlertContent(title);
        msg.setAppId(extAppId);
        msg.setDeviceToken(token);
        msg.setIosPushData(pushdata);
        PushResult result = null;
        try {
            log.debug(String.format("%s.调用点对点发送 .appId:[%s],extappId:[%s],context:[%s],token:[%s],retryId:[%s]", methodName, appId,
                    extAppId, packet.getContext(), token, packet.getDbRetryId()));
            result = apnsMsgPushService.pushAPNSMessage(msg);
        } catch (Exception e) {
            packetHandler.afterSendOperation(packet, false);
            log.error(String.format("%s.远程接口调用失败,异常信息:%s.内容:%s", methodName, e.getMessage(), packet.getContext()));
            return false;
        }
        // if use asynchronous ,the following code should be noted
        String errorCode = result.getErrorCode();
        if (errorCode.equals("0")) {
            flag = true;
            packetHandler.afterSendOperation(packet, flag);
        } else {
            flag = false;
            packetHandler.afterSendOperation(packet, flag, true);
        }
        //
        // log
        log.info(String.format("%s发送完成:结果:[%s--%s]***domainId:[%s].tempId:[%s].extparam:[%s].接收人:[%s-%s].发送内容:[%s].retryId:[%s]",
                methodName, result.getErrorCode(), result.getDesc(), packet.getDomainId(), packet.getTemplateId(), packet.getExtParam(),
                packet.getRecvUserId(), token, packet.getContext(), packet.getDbRetryId()));
        return flag;
    }

    @Override
    protected Tasks getTaskType() {
        return Tasks.FETCH_RETRY_APNS;
    }

    @Override
    protected String getQueueName() {
        return "apns推送处理队列";
    }

    @Override
    protected Tasks getFullTaskType() {
        return Tasks.FETCH_RETRY_APNS_FULL;
    }

    @Override
    protected String getFullQueueName() {
        return "apns全量推送处理队列";
    }

}
